/*
 * hamlib - (C) Frank Singleton 2000 (vk3fcs@ix.netcom.com)
 *
 * ft767gx.c - (C) Steve Conklin 2007
 * adapted from ft757gx.c  by Stephane Fillod 2004
 * This shared library provides an API for communicating
 * via serial interface to an FT-767GX using the "CAT" interface
 * box (FIF-232C) or similar
 *
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */


/*
 * TODO
 *
 * 1. Allow cached reads
 * 2. set_mem/get_mem, get_channel, set_split/get_split,
 * 	set_func/get_func
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */

#include "hamlib/rig.h"
#include "serial.h"
#include "misc.h"
#include "yaesu.h"
#include "ft767gx.h"


static int ft767_init(RIG *rig);
static int ft767_cleanup(RIG *rig);
static int ft767_open(RIG *rig);
static int ft767_close(RIG *rig);

static int ft767_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
static int ft767_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);

static int ft767_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width); /* select mode */
static int ft767_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width); /* get mode */

static int ft767_set_vfo(RIG *rig, vfo_t vfo); /* select vfo */
static int ft767_get_vfo(RIG *rig, vfo_t *vfo); /* get vfo */

static int ft767_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt);

static int ft767_set_ctcss_tone(RIG * rig, vfo_t vfo, tone_t tone);
static int ft767_get_ctcss_tone(RIG * rig, vfo_t vfo, tone_t *tone);
static int ft767_set_ctcss_sql(RIG * rig, vfo_t vfo, tone_t tone);
static int ft767_get_ctcss_sql(RIG * rig, vfo_t vfo, tone_t *tone);

static int ft767_set_split_freq(RIG * rig, vfo_t vfo, freq_t tx_freq);
static int ft767_get_split_freq(RIG * rig, vfo_t vfo, freq_t * tx_freq);
static int ft767_set_split_mode(RIG * rig, vfo_t vfo, rmode_t tx_mode, pbwidth_t tx_width);
static int ft767_get_split_mode(RIG * rig, vfo_t vfo, rmode_t * tx_mode, pbwidth_t * tx_width);
static int ft767_set_split_vfo(RIG * rig, vfo_t vfo, split_t split, vfo_t tx_vfo);
static int ft767_get_split_vfo(RIG * rig, vfo_t vfo, split_t * split, vfo_t *tx_vfo);

/* Private helper function prototypes */

static int ft767_send_block_and_ack(RIG *rig, unsigned char *cmd, size_t length);
static int ft767_get_update_data(RIG *rig);
static int ft767_enter_CAT(RIG *rig);
static int ft767_leave_CAT(RIG *rig);
static int ft767_set_split(RIG *rig, unsigned int split);
static unsigned char vfo2rig(RIG *rig, vfo_t vfo);
static vfo_t rig2vfo(unsigned char status);
static int mode2rig(RIG *rig, rmode_t mode);
static int rig2mode(RIG *rig, int md, rmode_t *mode, pbwidth_t *width);
/* static int ctcss2rig(RIG *rig, tone_t tone); */
static int rig2ctcss(RIG *rig, unsigned char tn, tone_t *tone);

/*
 * On the rig used by the author of this software, the values returned
 * by the rig for the CTCSS tones are different than those in the Yaesu
 * documentation. This could possibly be because he tone module in that
 * rig is an aftermarket board sold by the piexx company. Leaving the
 * following undefined uses the values discovered by experimentation. For
 * The original values defined in the Yaesu documentation, uncomment it.
 */
/* #define USE_YAESU_PUBLISHED_TONES */

/* Valid command codes */
#define CMD_CAT_SW	0x00
#define CMD_CHECK	0x01
#define CMD_UP10HZ	0x02
#define CMD_DN10HZ	0x03
#define CMD_PROG_UP	0x04
#define CMD_PROG_DN	0x05
#define CMD_BAND_UP	0x06
#define CMD_BAND_DN	0x07
#define CMD_FREQ_SET	0x08
#define CMD_VFOMR	0x09
#define CMD_MULTICMD	0x0A /* This command code overloaded */
#define CMD_TONE_SET	0x0C
#define CMD_ACK		0x0B

/* subcommands for command code 0x0A */
/* values 0 - 9 select memories */
#define SUBCMD_MEM0	0x00 /* 8 bytes returned */
#define SUBCMD_MEM1	0x01 /* 8 bytes returned */
#define SUBCMD_MEM2	0x02 /* 8 bytes returned */
#define SUBCMD_MEM3	0x03 /* 8 bytes returned */
#define SUBCMD_MEM4	0x04 /* 8 bytes returned */
#define SUBCMD_MEM5	0x05 /* 8 bytes returned */
#define SUBCMD_MEM6	0x06 /* 8 bytes returned */
#define SUBCMD_MEM7	0x07 /* 8 bytes returned */
#define SUBCMD_MEM8	0x08 /* 8 bytes returned */
#define SUBCMD_MEM9	0x09 /* 8 bytes returned */
#define SUBCMD_MODE_LSB	0x10 /* 8 bytes returned */
#define SUBCMD_MODE_USB	0x11 /* 8 bytes returned */
#define SUBCMD_MODE_CW	0x12 /* 8 bytes returned */
#define SUBCMD_MODE_AM	0x13 /* 8 bytes returned */
#define SUBCMD_MODE_FM	0x14 /* 8 bytes returned */
#define SUBCMD_MODE_FSK	0x15 /* 8 bytes returned */
#define SUBCMD_HG_HAM	0x20 /* ham coverage */
#define SUBCMD_HG_GEN	0x21 /* general coverage */
#define SUBCMD_SPLIT	0x30 /* toggle split */
#define SUBCMD_CLAR	0x40 /* toggle clarifier */
#define SUBCMD_MTOV	0x50 /* memory to VFO */
#define SUBCMD_VTOM	0x60 /* VFO to memory */
#define SUBCMD_SWAP	0x70 /* swap VFO, memory */
#define SUBCMD_ACLR	0x80 /* turn off SPLIT, CLAR< OFFSET */





/*
 * Some useful offsets in the status update map (offset)
 *
 * Status Update Chart, FT767GXII
 */
#define STATUS_FLAGS		0
#define STATUS_CURR_FREQ	1	/* Operating Frequency */
#define STATUS_CURR_TONE	5
#define STATUS_CURR_MODE	6
#define STATUS_VFOA_FREQ	14
#define STATUS_VFOA_MODE	19
#define STATUS_VFOB_FREQ	20
#define STATUS_VFOB_MODE	25

/* Status bit masks */
#define STATUS_MASK_PTT		0x01
#define STATUS_MASK_HG		0x02
#define STATUS_MASK_TXINH	0x04
#define STATUS_MASK_SPLIT	0x08
#define STATUS_MASK_VFO		0x10
#define STATUS_MASK_MEM		0x20
#define STATUS_MASK_CLAR	0x40
#define STATUS_MASK_CAT		0x80


/* mode byte */
#define MODE_LSB	0x0
#define MODE_USB	0x1
#define MODE_CW		0x2
#define MODE_AM		0x3
#define MODE_FM 	0x4
#define MODE_FSK 	0x5

/*
 * Things that I need to figure out how to fit in
 */
//#define VFO_OPERATIONS_LIST (RIG_OP_FROM_VFO | RIG_OP_TO_VFO | RIG_OP_TOGGLE)
/* These VFO OPS can only be applied to the current VFO */
//	RIG_OP_UP =		(1<<5),	/*!< UP increment VFO freq by tuning step*/
//	RIG_OP_DOWN =		(1<<6),	/*!< DOWN decrement VFO freq by tuning step*/
//	RIG_OP_BAND_UP =	(1<<7),	/*!< Band UP */

/*
#define FT767_MEM_CAP {	\
    .freq = 1, \
      .mode = 1, \
            .width = 1,
      .ctcss_tone = 1 \
      }
*/
/*
 * End of things not put in yet
 */


/*
 * Receiver caps
 */

#define FT767GX_ALL_RX_MODES (RIG_MODE_AM|RIG_MODE_CW|RIG_MODE_SSB|RIG_MODE_FM|RIG_MODE_PKTFM)
#define FT767GX_HF_RX_MODES (RIG_MODE_AM|RIG_MODE_CW|RIG_MODE_SSB)


/*
 * TX caps
 */

#define FT767GX_ALL_TX_MODES (RIG_MODE_AM|RIG_MODE_CW|RIG_MODE_SSB|RIG_MODE_FM|RIG_MODE_PKTFM)
#define FT767GX_HF_TX_MODES (RIG_MODE_AM|RIG_MODE_CW|RIG_MODE_SSB)

#define CTCSS_TONE_LIST \
  670,  710,  747,  770,  797,  825,  854,  885,  915,  948, \
 1000, 1035, 1072, 1109, 1148, 1188, 1230, 1273, 1318, 1365, \
 1413, 1462, 1514, 1567, 1622, 1679, 1738, 1799, 1862, 1928, \
 2035, 2107, 2181, 2257, 2336, 2418, 2503, 0

/*
 * future - private data
 *
 */

struct ft767_priv_data {
  unsigned char pacing;		/* pacing value */
  unsigned int read_update_delay;	 /* depends on pacing value */
  unsigned char current_vfo;	/* active VFO from last cmd , can be either RIG_VFO_A or RIG_VFO_B only */
  unsigned char update_data[FT767GX_STATUS_UPDATE_DATA_LENGTH]; /* returned data */
  unsigned char rx_data[FT767GX_STATUS_UPDATE_DATA_LENGTH]; /* returned data */
  unsigned char ack_cmd[5];
};

const tone_t static_767gx_ctcss_list[] = {
        CTCSS_TONE_LIST
};


/*
 * ft767gx rigs capabilities.
 * Also this struct is READONLY!
 */

const struct rig_caps ft767gx_caps = {
  .rig_model =        RIG_MODEL_FT767,
  .model_name =       "FT-767GX",
  .mfg_name =         "Yaesu",
  .version =           "1.0",
  .copyright =         "LGPL",
  .status =            RIG_STATUS_STABLE,
  .rig_type =          RIG_TYPE_TRANSCEIVER,
  .ptt_type =          RIG_PTT_RIG,
  .dcd_type =          RIG_DCD_NONE,
  .port_type =         RIG_PORT_SERIAL,
  .serial_rate_min =   4800,
  .serial_rate_max =   4800,
  .serial_data_bits =  8,
  .serial_stop_bits =  2,
  .serial_parity =     RIG_PARITY_NONE,
  .serial_handshake =  RIG_HANDSHAKE_NONE,
  .write_delay =       FT767GX_WRITE_DELAY,
  .post_write_delay =  FT767GX_POST_WRITE_DELAY,
  .timeout =           2000,
  .retry =             0,
  .has_get_func =      RIG_FUNC_NONE,
  .has_set_func =      RIG_FUNC_NONE,
  .has_get_level =     RIG_LEVEL_NONE,
  .has_set_level =     RIG_LEVEL_NONE,
  .has_get_parm =      RIG_PARM_NONE,
  .has_set_parm =      RIG_PARM_NONE,
  .ctcss_list =        static_767gx_ctcss_list,
  .dcs_list =          NULL,
  .preamp =            { RIG_DBLST_END, },
  .attenuator =        { RIG_DBLST_END, },
  .max_rit =           Hz(9999),
  .max_xit =           Hz(0),
  .max_ifshift =       Hz(0),
  .targetable_vfo =    0,
  .transceive =        RIG_TRN_OFF,
  .bank_qty =          0,
  .chan_desc_sz =      0,
  .chan_list =         {RIG_CHAN_END, }, /* TODO need memory channel capabilities here */
  //  .chan_list =         {0, 9, RIG_MTYPE_MEM, RIG_CHAN_END, }, /* TODO need memory channel capabilities here */

  .rx_range_list1 =    { RIG_FRNG_END, },    /* FIXME: enter region 1 setting */

  .tx_range_list1 =    { RIG_FRNG_END, },

  .rx_range_list2 =    { { .start = kHz(100), .end = 29999999,
			.modes = FT767GX_ALL_RX_MODES,.low_power = -1,.high_power = -1},
		      RIG_FRNG_END, }, /* rx range */

  .tx_range_list2 =    { {kHz(1500),1999900,FT767GX_HF_TX_MODES,.low_power = 5000,.high_power = 100000},

    {.start = kHz(3500),3999900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = kHz(7000),7499900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(10),10499900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(14),14499900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(18),18499900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(21),21499900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = kHz(24500),24999900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(28),29999900,FT767GX_HF_TX_MODES,5000,100000},

    {.start = MHz(50),59999900,FT767GX_ALL_TX_MODES,5000,10000},

    {.start = MHz(144),147999900,FT767GX_ALL_TX_MODES,5000,10000},

    {.start = MHz(430),449999990,FT767GX_ALL_TX_MODES,5000,10000},

    RIG_FRNG_END, },


  .tuning_steps =  {
    {FT767GX_ALL_RX_MODES,10},
    {FT767GX_ALL_RX_MODES,100},
    RIG_TS_END,
  },

      /* mode/filter list, .remember =  order matters! */
  .filters =  {
    RIG_FLT_END,
  },


  .priv =   NULL, /* private data */

  .rig_init =   ft767_init,
  .rig_cleanup =    ft767_cleanup,
  .rig_open =   ft767_open,				/* port opened */
  .rig_close =  ft767_close,				/* port closed */

  .set_freq =   ft767_set_freq,		/* set freq */
  .get_freq =   ft767_get_freq,		/* get freq */
  .set_mode =   ft767_set_mode,		/* set mode */
  .get_mode =   ft767_get_mode,		/* get mode */
  .set_vfo =    ft767_set_vfo,		/* set vfo */
  .get_vfo =    ft767_get_vfo,		/* get vfo */
  .set_ptt =    NULL,		/* set ptt */
  .get_ptt =    ft767_get_ptt,		/* get ptt */

  .set_ctcss_tone =	ft767_set_ctcss_tone,
  .get_ctcss_tone =	ft767_get_ctcss_tone,
  .set_ctcss_sql =	ft767_set_ctcss_sql,
  .get_ctcss_sql =	ft767_get_ctcss_sql,

  .set_split_freq =	ft767_set_split_freq,
  .get_split_freq =	ft767_get_split_freq,
  .set_split_mode =	ft767_set_split_mode,
  .get_split_mode =	ft767_get_split_mode,
  .set_split_vfo =	ft767_set_split_vfo,
  .get_split_vfo =	ft767_get_split_vfo,

};

/*
 * _init
 *
 */


int ft767_init(RIG *rig) {
  struct ft767_priv_data *p;

  if (!rig)
    return -RIG_EINVAL;

  p = (struct ft767_priv_data*)malloc(sizeof(struct ft767_priv_data));
  if (!p)			/* whoops! memory shortage! */
    return -RIG_ENOMEM;

  rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__);

  /* TODO: read pacing from preferences */

  p->pacing = FT767GX_PACING_DEFAULT_VALUE; /* set pacing to minimum for now */
  p->read_update_delay = FT767GX_DEFAULT_READ_TIMEOUT; /* set update timeout to safe value */
  p->current_vfo =  RIG_VFO_A;	/* default to VFO_A ? */
  p->ack_cmd[0] = 00;
  p->ack_cmd[1] = 00;
  p->ack_cmd[2] = 00;
  p->ack_cmd[3] = 00;
  p->ack_cmd[4] = 0x0B;
  rig->state.priv = (void*)p;

  return RIG_OK;
}


/*
 * ft767_cleanup routine
 * the serial port is closed by the frontend
 */

int ft767_cleanup(RIG *rig) {
  if (!rig)
    return -RIG_EINVAL;

  rig_debug(RIG_DEBUG_TRACE, "%s called\n", __FUNCTION__);

  if (rig->state.priv)
    free(rig->state.priv);
  rig->state.priv = NULL;

  return RIG_OK;
}

/*
 * ft767_open  routine
 *
 */

int ft767_open(RIG *rig)
{
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  serial_flush(&rig->state.rigport);

  /* send 0 delay PACING cmd to rig  */
  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	memset(priv->update_data,0,FT767GX_STATUS_UPDATE_DATA_LENGTH);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
	memset(priv->update_data,0,FT767GX_STATUS_UPDATE_DATA_LENGTH);
	return retval;
  }
  rig->state.vfo_list = RIG_VFO_A | RIG_VFO_B;
  return RIG_OK;
}

/*
 * ft767_close  routine
 *
 */

int ft767_close(RIG *rig)
{
  serial_flush(&rig->state.rigport);
  return RIG_OK;
}

/*
 * This command only functions when operating on a vfo.
 *
 */

int ft767_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_FREQ_SET};
  int retval;

  /* should the vfo be tested? */
  /* fill in first four bytes */
  to_bcd(cmd, freq/10, 8);

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return retval;
}

int ft767_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_MULTICMD};
  int retval;

  /* fill in p1 */
  cmd[3] = mode2rig(rig, mode);

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return retval;
}

/*
 * Return Freq
 */

int ft767_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
	  return retval;

  /* grab freq and convert */

  switch(vfo) {
  case RIG_VFO_CURR:
    *freq = from_bcd_be(priv->update_data+STATUS_CURR_FREQ, 8);
    break;
  case RIG_VFO_A:
    *freq = from_bcd_be(priv->update_data+STATUS_VFOA_FREQ, 8);
    break;
  case RIG_VFO_B:
    *freq = from_bcd_be(priv->update_data+STATUS_VFOB_FREQ, 8);
  break;
  default:
    return -RIG_EINVAL;		/* sorry, wrong VFO */
  }

  *freq *= 10.0; /* rig reads in 10 Hz increments*/

  return RIG_OK;
}



int ft767_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
{
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
	  return retval;

  switch(vfo) {
  case RIG_VFO_CURR:
    retval = rig2mode(rig, priv->update_data[STATUS_CURR_MODE], mode, width);
    break;
  case RIG_VFO_A:
    retval = rig2mode(rig, priv->update_data[STATUS_VFOA_MODE], mode, width);
    break;
  case RIG_VFO_B:
    retval = rig2mode(rig, priv->update_data[STATUS_VFOB_MODE], mode, width);
  break;
  default:
    return -RIG_EINVAL;		/* sorry, wrong VFO */
  }

  return retval;
}

/*
 * set vfo and store requested vfo for later RIG_VFO_CURR
 * requests.
 *
 */
int ft767_set_vfo(RIG *rig, vfo_t vfo) {
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_VFOMR};
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  switch(vfo) {
  case RIG_VFO_CURR:
	  return RIG_OK;
  case RIG_VFO_A:
	cmd[3] = 0x00;
	break;
  case RIG_VFO_B:
	cmd[3] = 0x01;
	break;
  default:
    return -RIG_EINVAL;		/* sorry, wrong VFO */
  }

  priv->current_vfo = vfo;

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return retval;
}

int ft767_get_vfo(RIG *rig, vfo_t *vfo) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
	  return retval;

  *vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);

  return RIG_OK;
}

int ft767_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt)
{
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
	  return retval;

  *ptt = priv->update_data[STATUS_FLAGS] & 0x01 ?  RIG_PTT_ON : RIG_PTT_OFF;
  return RIG_OK;
}

/* on this rig, only one tone can be set per VFO or memory,
 * and it's active for both tx and receive.
 */

int ft767_set_ctcss_tone(RIG * rig, vfo_t vfo, tone_t tone) {
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_TONE_SET};
  int retval;

  /* determine whether we have to send high-Q */
  switch(tone) {
  case 747:
  case 797:
  case 854:
  case 915:
    cmd[1] = 1; /* High Q */
    break;
  default: break;
  }

  /* fill in p2 and p1 with bcd tone value */
  to_bcd(&cmd[2], tone, 4);

  /*  cmd[3] = tone2rig(rig, tone); */

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return retval;
}

int ft767_get_ctcss_tone(RIG * rig, vfo_t vfo, tone_t *tone) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: get_update_data failed with status %d\n",
	      __FUNCTION__, retval);
    return retval;
  }
  retval = rig2ctcss(rig, priv->update_data[STATUS_CURR_TONE], tone);
  return retval;
}

int ft767_set_ctcss_sql(RIG * rig, vfo_t vfo, tone_t tone) {
  return ft767_set_ctcss_tone(rig, vfo, tone);
}

int ft767_get_ctcss_sql(RIG * rig, vfo_t vfo, tone_t *tone) {
  return ft767_get_ctcss_tone(rig, vfo, tone);
}

/* split functions */

int ft767_set_split_freq(RIG * rig, vfo_t vfo, freq_t tx_freq) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  unsigned char freq_cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_FREQ_SET};
  unsigned char vfo_cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_VFOMR};
  vfo_t curr_vfo;
  vfo_t change_vfo;
  unsigned char curr_split;
  int retval;

  /* This appears to always pass in VFO_CURR as the vfo */
  /* My decision is to only update the xmit VFO if we're in split mode */

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
    return retval;

  /* find out how we're currently configured */
  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);
  curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;

  if(curr_split) {
    switch(curr_vfo) {
      /* we need to set something */
    case RIG_VFO_A:
      change_vfo = RIG_VFO_B;
      break;
    case RIG_VFO_B:
      change_vfo = RIG_VFO_A;
      break;
    case RIG_VFO_MEM:
	rig_debug(RIG_DEBUG_ERR,"%s: error, in both split and memory modes\n", __FUNCTION__);
	return RIG_OK;
    default:
      rig_debug(RIG_DEBUG_ERR,"%s: error, unknown vfo value %d\n", __FUNCTION__, curr_vfo);
      return RIG_OK;
    }
  } else {
    /* not in split mode, do nothing */
    return RIG_OK;
  }

  /* fill in first four bytes */
  to_bcd(freq_cmd, tx_freq/10, 8);

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  /* change to the xmit VFO */
  vfo_cmd[3] = vfo2rig(rig, change_vfo);
  retval =  ft767_send_block_and_ack(rig, vfo_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  /* change the freq */
  retval =  ft767_send_block_and_ack(rig, freq_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  /* change back to the rcv VFO */
  vfo_cmd[3] = vfo2rig(rig, curr_vfo);
  retval =  ft767_send_block_and_ack(rig, vfo_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return RIG_OK;
}

int ft767_get_split_freq(RIG * rig, vfo_t vfo, freq_t * tx_freq) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;
  unsigned int offset;
  vfo_t curr_vfo;
  unsigned char curr_split;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: get_update_data failed with status %d\n",
	      __FUNCTION__, retval);
    return retval;
  }

  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);
  curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;

  if(curr_split) {
    switch(curr_vfo) {
      /* we need to get something */
    case RIG_VFO_A:
      offset = STATUS_VFOB_FREQ;
      break;
    case RIG_VFO_B:
      offset = STATUS_VFOA_FREQ;
      break;
    case RIG_VFO_MEM:
	rig_debug(RIG_DEBUG_ERR,"%s: error, in both split and memory modes\n", __FUNCTION__);
	return RIG_OK;
    default:
      rig_debug(RIG_DEBUG_ERR,"%s: error, unknown vfo value %d\n", __FUNCTION__, curr_vfo);
      return RIG_OK;
    }
  } else {
    /* not in split mode, do nothing */
    return RIG_OK;
  }
  *tx_freq = from_bcd_be(priv->update_data+offset, 8);

  return RIG_OK;
}

int ft767_set_split_mode(RIG * rig, vfo_t vfo, rmode_t tx_mode, pbwidth_t tx_width) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  unsigned char mode_cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_MULTICMD};
  unsigned char vfo_cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_VFOMR};
  vfo_t curr_vfo;
  vfo_t change_vfo;
  unsigned char curr_split;
  int retval;

  /* This appears to always pass in VFO_CURR as the vfo */
  /* My decision is to only update the xmit mode if we're in split mode */

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
    return retval;

  /* find out how we're currently configured */
  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);
  curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;

  if(curr_split) {
    switch(curr_vfo) {
      /* we need to set something */
    case RIG_VFO_A:
      change_vfo = RIG_VFO_B;
      break;
    case RIG_VFO_B:
      change_vfo = RIG_VFO_A;
      break;
    case RIG_VFO_MEM:
	rig_debug(RIG_DEBUG_ERR,"%s: error, in both split and memory modes\n", __FUNCTION__);
	return RIG_OK;
    default:
      rig_debug(RIG_DEBUG_ERR,"%s: error, unknown vfo value %d\n", __FUNCTION__, curr_vfo);
      return RIG_OK;
    }
  } else {
    /* not in split mode, do nothing */
    return RIG_OK;
  }

  /* fill in p1 */
  mode_cmd[3] = mode2rig(rig, tx_mode);

  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  /* change to the xmit VFO */
  vfo_cmd[3] = vfo2rig(rig, change_vfo);
  retval =  ft767_send_block_and_ack(rig, vfo_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send vfo change 1 command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  /* change the mode */
  retval =  ft767_send_block_and_ack(rig, mode_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send mode command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  /* change back to the rcv VFO */
  vfo_cmd[3] = vfo2rig(rig, curr_vfo);
  retval =  ft767_send_block_and_ack(rig, vfo_cmd, YAESU_CMD_LENGTH);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send vfo change 2command: status %d\n",
			__FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
  }
  return RIG_OK;
}

int ft767_get_split_mode(RIG * rig, vfo_t vfo, rmode_t * tx_mode, pbwidth_t * tx_width) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;
  unsigned int offset;
  vfo_t curr_vfo;
  unsigned char curr_split;

  retval = ft767_get_update_data(rig);
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: get_update_data failed with status %d\n",
	      __FUNCTION__, retval);
    return retval;
  }

  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);
  curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;

  if(curr_split) {
    switch(curr_vfo) {
      /* we need to get something */
    case RIG_VFO_A:
      offset = STATUS_VFOB_MODE;
      break;
    case RIG_VFO_B:
      offset = STATUS_VFOA_MODE;
      break;
    case RIG_VFO_MEM:
	rig_debug(RIG_DEBUG_ERR,"%s: error, in both split and memory modes\n", __FUNCTION__);
	return RIG_OK;
    default:
      rig_debug(RIG_DEBUG_ERR,"%s: error, unknown vfo value %d\n", __FUNCTION__, curr_vfo);
      return RIG_OK;
    }
  } else {
    /* not in split mode, do nothing */
    return RIG_OK;
  }
  /* get the actual mode */
  retval = rig2mode(rig, priv->update_data[offset], tx_mode, tx_width);
  return retval;
}

int ft767_set_split_vfo(RIG * rig, vfo_t vfo, split_t split, vfo_t tx_vfo) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, 0x00};
  int retval;
  vfo_t curr_vfo;
  vfo_t future_vfo;
  unsigned char curr_split;
  rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);

  if (!rig)
    return -RIG_EINVAL;

  rig_debug(RIG_DEBUG_TRACE, "%s: passed vfo = 0x%02x\n", __func__, vfo);
  rig_debug(RIG_DEBUG_TRACE, "%s: passed tx_vfo = 0x%02x\n", __func__, tx_vfo);
  rig_debug(RIG_DEBUG_TRACE, "%s: passed split = 0x%02x\n", __func__, split);

  if ((tx_vfo != RIG_VFO_A) && (tx_vfo != RIG_VFO_B))
    return -RIG_EINVAL;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0)
    return retval;

  /* find out which VFO we're currently using */
  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);

  /*
   * If split is enabled, Set the current VFO to the opposite of
   * the one specified in tx_vfo. If split is not enabled, then don't change anything.
  */

  switch(split) {
  case RIG_SPLIT_OFF:
    /* turn off split, leave everything else alone */
    return ft767_set_split(rig, 0);
    break;
  case RIG_SPLIT_ON:
    switch(tx_vfo) {
    case RIG_VFO_CURR:
      // we need to switch VFOs
      if (curr_vfo == RIG_VFO_A)
	future_vfo = RIG_VFO_B;
      else if (curr_vfo == RIG_VFO_B)
	future_vfo = RIG_VFO_B;
      else {
	/* Currently using memory! */
	rig_debug(RIG_DEBUG_ERR, "%s: RIG_VFO_CURR requested when it is a memory\n", __func__);
	return -RIG_EINVAL;
      }
      break;
    case RIG_VFO_A:
      future_vfo = RIG_VFO_B;
      break;
    case RIG_VFO_B:
      future_vfo = RIG_VFO_A;
      break;
    default:
      return -RIG_EINVAL;		/* sorry, wrong VFO */
    }

    serial_flush(&rig->state.rigport);

    retval = ft767_enter_CAT(rig);
    if (retval < 0) {
      rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
      return retval;
    }

    /* See whether we need to toggle the split state */
    curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;
    if (curr_split) curr_split = RIG_SPLIT_ON;

    if (curr_split != split) {
      cmd[3] = SUBCMD_SPLIT;
      cmd[4] = CMD_MULTICMD;
      retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
      if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send split command: status %d\n",
		  __FUNCTION__, retval);
	return retval;
      }
    }

    /* now set the rx vfo if needed */
    if (curr_vfo != future_vfo) {
      cmd[3] = vfo2rig(rig, future_vfo);
      cmd[4] = CMD_VFOMR;
      retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
      if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send set vfo command: status %d\n",
		  __FUNCTION__, retval);
	return retval;
      }
    }

    /* Make sure clarifier is off */
    if (priv->update_data[STATUS_FLAGS] & STATUS_MASK_CLAR) {
      cmd[3] = SUBCMD_CLAR;
      cmd[4] = CMD_MULTICMD;
      retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
      if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: failed to send set clar command: status %d\n",
		  __FUNCTION__, retval);
	return retval;
      }
    }

    retval = ft767_leave_CAT(rig);
    if (retval < 0) {
      rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
      return retval;
    }
    break;
  default:
    return -RIG_EINVAL;
  }
  return RIG_OK;
}

int ft767_get_split_vfo(RIG * rig, vfo_t vfo, split_t * split, vfo_t *tx_vfo) {
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;
  vfo_t curr_vfo;

  retval = ft767_get_update_data(rig);	/* get whole shebang from rig */
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: get_update_data failed with status %d\n",
	      __FUNCTION__, retval);
    return retval;
  }

  /* TODO if SPLIT is enabled, return which VFO is the transmit VFO!!
   */

  if (priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT)
    *split = RIG_SPLIT_ON;
  else
    *split = RIG_SPLIT_OFF;

  curr_vfo = rig2vfo(priv->update_data[STATUS_FLAGS]);
  switch (curr_vfo) {
  case RIG_VFO_A:
    *tx_vfo = RIG_VFO_B;
    break;
  case RIG_VFO_B:
    *tx_vfo = RIG_VFO_A;
    break;
  default:
    /* we don't know how to deal with MEM, anything else is an error */
    /* TODO make sure this is what we want to do here */
    rig_debug(RIG_DEBUG_ERR,"%s: current vfo is %d with split\n", __FUNCTION__, curr_vfo);
    return -RIG_EINVAL;
    break;
  }

  return RIG_OK;
}

/* End of hamlib API-mapped functions */


/*
 * This function puts the radio in CAT mode
 */
int ft767_enter_CAT(RIG *rig) {
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x00, CMD_CAT_SW};
  rig_debug(RIG_DEBUG_TRACE, "%s: Entered\n", __FUNCTION__);
  return ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
}

/*
 * This function takes the radio out of CAT mode
 */
int ft767_leave_CAT(RIG *rig) {
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x01, CMD_CAT_SW};
  rig_debug(RIG_DEBUG_TRACE, "%s: Entered\n", __FUNCTION__);
  return ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
}


/*
 * The Yaesu interface is convoluted and braindead.
 *
 * Private helper function. The 767GX has a handshaking system that works like this:
 *
 * 5 byte command block sent to rig
 * (5 to 20 mS delay)
 * Rig echos 5 byte command block back
 * Send a 5 byte ACK block to the rig
 * (5 to 20 mS delay)
 *
 * Rig sends back a status update block (5-86 bytes)
 * The status update block is received in reverse byte order from the way it's structured
 *
 * In addition, You must send a command to enable CAT mode, and disable when done.
 * When in CAT mode, the front panel of the radio is locked out.
 *
 * There is an error in the manual, the response length for a TONE SET command
 * is 26 bytes, not 5.
 */

int ft767_send_block_and_ack(RIG *rig, unsigned char *cmd, size_t length)
{
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  size_t replylen, cpycnt;
  unsigned char cmd_echo_buf[5];
  int retval;
  unsigned char *src, *dst;

  /* Validate command and set length of data returned */
  switch(cmd[4]) {
  case CMD_CHECK:
  case CMD_CAT_SW:
    replylen = 86;
    break;
  case CMD_UP10HZ:
  case CMD_DN10HZ:
  case CMD_PROG_UP:
  case CMD_PROG_DN:
  case CMD_BAND_UP:
  case CMD_BAND_DN:
  case CMD_FREQ_SET:
  case CMD_VFOMR:
  case CMD_ACK:
    replylen = 5;
    break;
  case CMD_TONE_SET:
    replylen = 26; /* the manual is wrong */
    break;

  case CMD_MULTICMD:
    if (cmd[3] <= 0x15) {
      replylen = 8;
    } else {
      switch (cmd[3]) {
      case SUBCMD_HG_HAM:
      case SUBCMD_HG_GEN:
      case SUBCMD_SPLIT:
      case SUBCMD_CLAR:
      case SUBCMD_MTOV:
	replylen = 26;
	break;
      case SUBCMD_VTOM:
	replylen = 68;
	break;
      case SUBCMD_SWAP:
      case SUBCMD_ACLR:
	replylen = 5;
	break;
      default:
	/* invalid or unknown sub-command */
	rig_debug(RIG_DEBUG_ERR,"%s: invalid sub-command 0x%x for command 0x%x\n",
		  __FUNCTION__, cmd[3], cmd[4]);
	return -RIG_EINVAL;
      }
    }
    break;
  default:
    /* invalid or unknown command */
    rig_debug(RIG_DEBUG_ERR,"%s: invalid command 0x%x\n",
	      __FUNCTION__, cmd[4]);
    return -RIG_EINVAL;
  }

  /* send the command block */
  write_block(&rig->state.rigport, (char *) cmd, YAESU_CMD_LENGTH);

  /* read back the command block echo */
  retval = read_block(&rig->state.rigport,
		      (char *) cmd_echo_buf,
		      YAESU_CMD_LENGTH);

  /* see if it matches the command we sent */
  if (memcmp(cmd_echo_buf, cmd, YAESU_CMD_LENGTH)) {
    rig_debug(RIG_DEBUG_ERR,"%s: Command echo doesn't match\n",
	      __FUNCTION__);
    return -RIG_EINVAL;
  }

  /* send the ACK */
  write_block(&rig->state.rigport, (char *) priv->ack_cmd, YAESU_CMD_LENGTH);

  /* read back the response (status bytes) */
  retval = read_block(&rig->state.rigport,
		      (char *) priv->rx_data,
		      replylen);
  // update data
  if (retval != replylen) {
    rig_debug(RIG_DEBUG_ERR,"%s: Got unexpected number of bytes %d in response\n",
	      __FUNCTION__, retval);
    return -RIG_EINVAL;
  }

  /* reverse the data buffer returned from the rig */
  src = &priv->rx_data[0];
  dst = &priv->update_data[replylen-1];
  cpycnt = replylen;
  while (cpycnt--)
    *dst-- = *src++;

  return RIG_OK;
}

/*
 * private helper function. Retrieves update data from rig.
 * using pacing value and buffer indicated in *priv struct.
 *
 * need to use this when doing ft767_get_* stuff
 */


int ft767_get_update_data(RIG *rig)
{
  /*  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, 0x01, CMD_CHECK}; */
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;

  serial_flush(&rig->state.rigport);

  /* Entering CAT updates our data structures */
  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
	rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
	return retval;
  }

  rig_debug(RIG_DEBUG_TRACE, "%s: status = 0x%02x\n", __func__, priv->update_data[STATUS_FLAGS]);

  return RIG_OK;
}

int ft767_set_split(RIG *rig, unsigned int split)
{
  unsigned char cmd[YAESU_CMD_LENGTH] = { 0x00, 0x00, 0x00, SUBCMD_SPLIT, CMD_MULTICMD};
  struct ft767_priv_data *priv = (struct ft767_priv_data*)rig->state.priv;
  int retval;
  unsigned int curr_split;

  serial_flush(&rig->state.rigport);

  /* Entering CAT updates our data structures */
  retval = ft767_enter_CAT(rig);
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: enter_CAT %d\n", __FUNCTION__, retval);
    return retval;
  }

  /* See whether we need to toggle */
  curr_split = priv->update_data[STATUS_FLAGS] & STATUS_MASK_SPLIT;

  rig_debug(RIG_DEBUG_TRACE,"%s called curr_split = %d, split = %d\n", __FUNCTION__, curr_split, split);

  if (curr_split ^ split) {
    retval =  ft767_send_block_and_ack(rig, cmd, YAESU_CMD_LENGTH);
    if (retval < 0) {
      rig_debug(RIG_DEBUG_ERR,"%s: failed to send command: status %d\n",
		__FUNCTION__, retval);
      return retval;
    }
  }
  retval = ft767_leave_CAT(rig);
  if (retval < 0) {
    rig_debug(RIG_DEBUG_ERR,"%s: leave_CAT %d\n", __FUNCTION__, retval);
    return retval;
  }

  return RIG_OK;
}

unsigned char vfo2rig(RIG *rig, vfo_t vfo) {
  switch(vfo) {
  case RIG_VFO_CURR:
	  return RIG_OK;
  case RIG_VFO_A:
	return 0x00;
	break;
  case RIG_VFO_B:
	return 0x01;
	break;
  case RIG_VFO_MEM:
	return 0x02;
	break;
  default:
    return -RIG_EINVAL;		/* sorry, wrong VFO */
  }
}

vfo_t rig2vfo(unsigned char status) {
  if (status & 0x20)
    return RIG_VFO_MEM;
  else
    if (status & 0x10)
      return RIG_VFO_B;
    else
      return RIG_VFO_A;
  }

int mode2rig(RIG *rig, rmode_t mode)
{
  int md;

  /*
   * translate mode from generic to ft767 specific
   */
  switch(mode) {
  case RIG_MODE_LSB:	md = SUBCMD_MODE_LSB; break;
  case RIG_MODE_USB:	md = SUBCMD_MODE_USB; break;
  case RIG_MODE_CW:	md = SUBCMD_MODE_CW; break;
  case RIG_MODE_AM:	md = SUBCMD_MODE_AM; break;
  case RIG_MODE_FM:	md = SUBCMD_MODE_FM; break;
  case RIG_MODE_PKTFM:	md = SUBCMD_MODE_FSK; break;
  default:
    return -RIG_EINVAL;         /* sorry, wrong MODE */
  }
  return md;
}

  int rig2mode(RIG *rig, int md, rmode_t *mode, pbwidth_t *width)
{
  /*
   * translate mode from ft767 specific to generic
   */
  switch(md & 0x07) {
  case MODE_LSB:	*mode = RIG_MODE_LSB; break;
  case MODE_USB:	*mode = RIG_MODE_USB; break;
  case MODE_CW:		*mode = RIG_MODE_CW; break;
  case MODE_AM:		*mode = RIG_MODE_AM; break;
  case MODE_FM:		*mode = RIG_MODE_FM; break;
  case MODE_FSK:	*mode = RIG_MODE_PKTFM; break;
  default:
    return -RIG_EINVAL;         /* sorry, wrong MODE */
  }
  return RIG_OK;
}

int rig2ctcss(RIG *rig, unsigned char tn, tone_t *tone) {
  /*
   * translate tone from ft767 specific to generic
   */
  switch(tn) {
#ifdef USE_YAESU_PUBLISHED_TONES
    /* Yaesu documentation */
  case 0x3E: *tone = 670; break;
  case 0x1D: *tone = 670; break; /* High Q */
  case 0x3D: *tone = 719; break;
  case 0x1C: *tone = 719; break; /* High Q */
  case 0x1B: *tone = 747; break; /* High Q */
  case 0x3C: *tone = 770; break;
  case 0x1A: *tone = 770; break; /* High Q */
  case 0x19: *tone = 797; break; /* High Q */
  case 0x3B: *tone = 825; break;
  case 0x18: *tone = 825; break; /* High Q */
  case 0x17: *tone = 854; break; /* High Q */
  case 0x3A: *tone = 885; break;
  case 0x16: *tone = 885; break; /* High Q */
  case 0x15: *tone = 915; break; /* High Q */
  case 0x39: *tone = 948; break;
  case 0x38: *tone = 1000; break;
  case 0x37: *tone = 1035; break;
  case 0x36: *tone = 1072; break;
  case 0x35: *tone = 1109; break;
  case 0x34: *tone = 1148; break;
  case 0x33: *tone = 1188; break;
  case 0x32: *tone = 1230; break;
  case 0x31: *tone = 1273; break;
  case 0x30: *tone = 1318; break;
  case 0x2F: *tone = 1365; break;
  case 0x2E: *tone = 1413; break;
  case 0x2D: *tone = 1462; break;
  case 0x2C: *tone = 1514; break;
  case 0x2B: *tone = 1567; break;
  case 0x2A: *tone = 1622; break;
  case 0x29: *tone = 1679; break;
  case 0x28: *tone = 1738; break;
  case 0x27: *tone = 1799; break;
  case 0x26: *tone = 1862; break;
  case 0x25: *tone = 1928; break;
  case 0x24: *tone = 2035; break;
  case 0x23: *tone = 2107; break;
  case 0x22: *tone = 2181; break;
  case 0x21: *tone = 2257; break;
  case 0x20: *tone = 2336; break;
  case 0x1F: *tone = 2418; break;
  case 0x1E: *tone = 2503; break;
#else
    /* values found by experimentation */
  case  0: *tone = 670; break;
  case 33: *tone = 670; break; /* High Q */
  case 01: *tone = 719; break;
  case 34: *tone = 719; break; /* High Q */
  case 35: *tone = 747; break; /* High Q */
  case  2: *tone = 770; break;
  case 36: *tone = 770; break; /* High Q */
  case 37: *tone = 797; break; /* High Q */
  case  3: *tone = 825; break;
  case 38: *tone = 825; break; /* High Q */
  case 39: *tone = 854; break; /* High Q */
  case  4: *tone = 885; break;
  case 40: *tone = 885; break; /* High Q */
  case 41: *tone = 915; break; /* High Q */
  case  5: *tone = 948; break;
  case  6: *tone = 1000; break;
  case  7: *tone = 1035; break;
  case  8: *tone = 1072; break;
  case  9: *tone = 1109; break;
  case 10: *tone = 1148; break;
  case 11: *tone = 1188; break;
  case 12: *tone = 1230; break;
  case 13: *tone = 1273; break;
  case 14: *tone = 1318; break;
  case 15: *tone = 1365; break;
  case 16: *tone = 1413; break;
  case 17: *tone = 1462; break;
  case 18: *tone = 1514; break;
  case 19: *tone = 1567; break;
  case 20: *tone = 1622; break;
  case 21: *tone = 1679; break;
  case 22: *tone = 1738; break;
  case 23: *tone = 1799; break;
  case 24: *tone = 1862; break;
  case 25: *tone = 1928; break;
  case 26: *tone = 2035; break;
  case 27: *tone = 2107; break;
  case 28: *tone = 2181; break;
  case 29: *tone = 2257; break;
  case 30: *tone = 2336; break;
  case 31: *tone = 2418; break;
  case 32: *tone = 2503; break;
#endif
  default:
    rig_debug(RIG_DEBUG_ERR,"%s: Invalid tone value from rig: 0x%02x\n",
	      __FUNCTION__, tn);
    return -RIG_EINVAL;         /* sorry, wrong TONE */
    break;
  }
  return RIG_OK;
}
